/*
 * Copyright 2011-2012 Rivoli team
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.googlecode.rivoli.model.support

import com.googlecode.rivoli.RivoliException
import com.googlecode.rivoli.model.AttributeDescriptor
import com.googlecode.rivoli.model.ElementDescriptor

import org.testng.annotations.Test

class ElementDescriptorSupportTest {
	@Test
	void 'new element descriptor should not contain any attribute descriptors'() {
		ElementDescriptor elementDescriptor = new ElementDescriptorSupport('element')

		assert elementDescriptor.attributeDescriptors.empty
	}

	@Test
	void 'attribute descriptor should be remembered'() {
		ElementDescriptorSupport elementDescriptor = new ElementDescriptorSupport('car')

		elementDescriptor.addAttributeDescriptor('brand', String)

		assert elementDescriptor.attributeDescriptors.size() == 1

		AttributeDescriptor attributeDescriptor = elementDescriptor.attributeDescriptors.toList().first()

		assert attributeDescriptor.name == 'brand'
		assert attributeDescriptor.type == String
	}

	@Test(expectedExceptions = RivoliException)
	void 'duplicate attribute descriptor should not be accepted'() {
		ElementDescriptorSupport elementDescriptor = new ElementDescriptorSupport('car')

		elementDescriptor.addAttributeDescriptor('brand', String)
		elementDescriptor.addAttributeDescriptor('brand', String)
	}

	@Test(expectedExceptions = RivoliException)
	void 'duplicate attribute descriptor from parent should not be accepted'() {
		ElementDescriptorSupport vehicleDescriptor = new ElementDescriptorSupport('vehicle')
		ElementDescriptorSupport carDescriptor = new ElementDescriptorSupport('car')

		vehicleDescriptor.addAttributeDescriptor('color', String)
		carDescriptor.parent = vehicleDescriptor
		carDescriptor.addAttributeDescriptor('color', String)
	}

	@Test(expectedExceptions = RivoliException)
	void 'duplicate attribute descriptor from child should not be accepted'() {
		ElementDescriptorSupport vehicleDescriptor = new ElementDescriptorSupport('vehicle')
		ElementDescriptorSupport carDescriptor = new ElementDescriptorSupport('car')

		carDescriptor.addAttributeDescriptor('color', String)
		carDescriptor.parent = vehicleDescriptor
		vehicleDescriptor.addAttributeDescriptor('color', String)
	}

	@Test
	void 'attribute descriptor should be findable'() {
		ElementDescriptorSupport carDescriptor = new ElementDescriptorSupport('car')

		carDescriptor.addAttributeDescriptor('color', String)

		assert carDescriptor.findAttributeDescriptor('color')
	}

	@Test
	void 'attribute descriptor from ancestor should be findable'() {
		ElementDescriptorSupport vehicleDescriptor = new ElementDescriptorSupport('vehicle')
		ElementDescriptorSupport carDescriptor = new ElementDescriptorSupport('car')

		vehicleDescriptor.addAttributeDescriptor('color', String)
		carDescriptor.parent = vehicleDescriptor

		assert carDescriptor.findAttributeDescriptor('color')
	}

	@Test
	void 'unknown attribute descriptor should not be findable'() {
		ElementDescriptorSupport carDescriptor = new ElementDescriptorSupport('car')

		assert !carDescriptor.findAttributeDescriptor('color')
	}

	@Test
	void 'new element descriptor should be an orphan'() {
		ElementDescriptor elementDescriptor = new ElementDescriptorSupport('element')

		assert !elementDescriptor.parent
	}

	@Test(expectedExceptions = RivoliException)
	void 'element descriptor should be able to have a parent'() {
		ElementDescriptorSupport vehicleDescriptor = new ElementDescriptorSupport('vehicle')
		ElementDescriptorSupport carDescriptor = new ElementDescriptorSupport('car')

		vehicleDescriptor.addAttributeDescriptor('color', String)
		carDescriptor.parent = vehicleDescriptor
		carDescriptor.addAttributeDescriptor('color', String)
	}

	@Test(expectedExceptions = RivoliException)
	void 'setting parent should not be allowed with duplicate attribute descriptors'() {
		ElementDescriptorSupport vehicleDescriptor = new ElementDescriptorSupport('vehicle')
		ElementDescriptorSupport carDescriptor = new ElementDescriptorSupport('car')

		vehicleDescriptor.addAttributeDescriptor('color', String)
		carDescriptor.addAttributeDescriptor('color', String)
		carDescriptor.parent = vehicleDescriptor
	}

	@Test(expectedExceptions = RivoliException)
	void 'setting parent should not be allowed with duplicate attribute descriptors from ancestors'() {
		ElementDescriptorSupport vehicleDescriptor = new ElementDescriptorSupport('vehicle')
		ElementDescriptorSupport carDescriptor = new ElementDescriptorSupport('car')
		ElementDescriptorSupport vanDescriptor = new ElementDescriptorSupport('van')

		vehicleDescriptor.addAttributeDescriptor('color', String)
		vanDescriptor.addAttributeDescriptor('color', String)
		vanDescriptor.parent = carDescriptor
		carDescriptor.parent = vehicleDescriptor
	}

	@Test(expectedExceptions = RivoliException)
	void 'setting parent that is a container should not be allowed if element descriptor already is a container'() {
		ElementDescriptorSupport vehicleDescriptor = new ElementDescriptorSupport('vehicle')
		ElementDescriptorSupport carDescriptor = new ElementDescriptorSupport('car')
		ElementDescriptor passengerDescriptor = new ElementDescriptorSupport('passenger')
		ElementDescriptor engineDescriptor = new ElementDescriptorSupport('engine')

		vehicleDescriptor.containerFor = passengerDescriptor
		carDescriptor.containerFor = engineDescriptor

		carDescriptor.parent = vehicleDescriptor
	}

	@Test(expectedExceptions = RivoliException)
	void 'setting parent that is a container should not be allowed if one of its descendants is a container'() {
		ElementDescriptorSupport vehicleDescriptor = new ElementDescriptorSupport('vehicle')
		ElementDescriptorSupport carDescriptor = new ElementDescriptorSupport('car')
		ElementDescriptorSupport vanDescriptor = new ElementDescriptorSupport('van')
		ElementDescriptor passengerDescriptor = new ElementDescriptorSupport('passenger')
		ElementDescriptor goodsDescriptor = new ElementDescriptorSupport('goods')

		vehicleDescriptor.containerFor = passengerDescriptor
		vanDescriptor.containerFor = goodsDescriptor

		vanDescriptor.parent = carDescriptor
		carDescriptor.parent = vehicleDescriptor
	}

	@Test
	void 'new element descriptor should not be a container'() {
		ElementDescriptor elementDescriptor = new ElementDescriptorSupport('element')

		assert !elementDescriptor.containerFor
	}

	@Test
	void 'element descriptor should be able to be a container'() {
		ElementDescriptorSupport carDescriptor = new ElementDescriptorSupport('car')
		ElementDescriptor engineDescriptor = new ElementDescriptorSupport('engine')

		carDescriptor.containerFor = engineDescriptor

		assert carDescriptor.containerFor == engineDescriptor
	}

	@Test
	void 'element descriptor should be able to contain the same elements as its parent'() {
		ElementDescriptorSupport vehicleDescriptor = new ElementDescriptorSupport('vehicle')
		ElementDescriptorSupport carDescriptor = new ElementDescriptorSupport('car')
		ElementDescriptor passengerDescriptor = new ElementDescriptorSupport('passenger')

		vehicleDescriptor.containerFor = passengerDescriptor
		carDescriptor.parent = vehicleDescriptor

		assert carDescriptor.containerFor == passengerDescriptor
	}

	@Test(expectedExceptions = RivoliException)
	void 'element descriptor should not be able to be a container when one of its ancestors already is'() {
		ElementDescriptorSupport vehicleDescriptor = new ElementDescriptorSupport('vehicle')
		ElementDescriptorSupport carDescriptor = new ElementDescriptorSupport('car')
		ElementDescriptor passengerDescriptor = new ElementDescriptorSupport('passenger')
		ElementDescriptor engineDescriptor = new ElementDescriptorSupport('engine')

		vehicleDescriptor.containerFor = passengerDescriptor
		carDescriptor.parent = vehicleDescriptor
		carDescriptor.containerFor = engineDescriptor
	}

	@Test(expectedExceptions = RivoliException)
	void 'element descriptor should not be able to be a container when one of its descendants already is'() {
		ElementDescriptorSupport vehicleDescriptor = new ElementDescriptorSupport('vehicle')
		ElementDescriptorSupport carDescriptor = new ElementDescriptorSupport('car')
		ElementDescriptor passengerDescriptor = new ElementDescriptorSupport('passenger')
		ElementDescriptor engineDescriptor = new ElementDescriptorSupport('engine')

		carDescriptor.parent = vehicleDescriptor
		carDescriptor.containerFor = engineDescriptor
		vehicleDescriptor.containerFor = passengerDescriptor
	}

	@Test
	void 'element descriptor can have a renderer'() {
		Closure renderer = {}

		ElementDescriptor vehicleDescriptor = new ElementDescriptorSupport('vehicle', [renderer] as Set)

		assert vehicleDescriptor.getRenderer(Closure) == renderer
	}

	@Test
	void 'element descriptor can have multiple renderers'() {
		Closure renderer = {}
		TestRenderer testRenderer = [:] as TestRenderer

		ElementDescriptor vehicleDescriptor = new ElementDescriptorSupport('vehicle', [renderer, testRenderer] as Set)

		assert vehicleDescriptor.getRenderer(Closure) == renderer
		assert vehicleDescriptor.getRenderer(TestRenderer) == testRenderer
	}

	@Test
	void 'getting unsupported renderers should result in null'() {
		ElementDescriptor vehicleDescriptor = new ElementDescriptorSupport('vehicle')

		assert !vehicleDescriptor.getRenderer(TestRenderer)
	}

	private static class TestRenderer {}
}